/*
 * @(#)FreemarkerConfigFactory.java $version 2010. 8. 11
 *
 * Copyright 2007-2010 NHN Corp. All rights Reserved. 
 * NHN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.nhncorp.usf.core.result.helper;

import java.io.File;
import java.io.IOException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.imjavaj.usfim.freemarker.PatternResourceTemplateLoader;
import com.nhncorp.lucy.common.config.model.ApplicationInfo;
import com.nhncorp.lucy.web.util.FreeMarkerUtil;
import com.nhncorp.usf.core.Constants;
import com.nhncorp.usf.core.resource.ResourceTemplateLoader;
import com.nhncorp.usf.core.result.template.FreemarkerResult;
import com.nhncorp.usf.core.servlet.ServletContextHolder;
import com.nhncorp.usf.core.util.StringUtil;

import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.cache.WebappTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.ObjectWrapper;
import freemarker.template.TemplateExceptionHandler;

/**
 * {@link FreemarkerResult} 에서 사용될 {@link Configuration} 생성 및 반환.
 * 
 * @author Web Platform Development Team
 */
public class FreemarkerConfigFactory {
    static Log log = LogFactory.getLog(FreemarkerConfigFactory.class);

    /**
     * @author Web Platform Development Team
     * @version $Rev: 10957 $, $Date: 2010-08-11 16:28:15 +0900 (수, 11 8 2010) $
     */
    private static final class ConfigurationHolder {
        static Configuration configuration;

        static {
            try {
                init();
            } catch (FreemarkerInitializeException e) {
                log.error(e);
            }
        }

        private static void init() throws FreemarkerInitializeException {
            configuration = new Configuration();
            configuration.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);
            configuration
                    .setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
            configuration.setDefaultEncoding("utf-8");
            log.debug("ApplicationInfo's file encoding value is "
                    + ApplicationInfo.getFileEncoding());

            if (!FreeMarkerUtil.setSettingsToConfiguration(configuration)
                    && StringUtil.isNotEmpty(ApplicationInfo.getFileEncoding())) {
                configuration.setDefaultEncoding(ApplicationInfo
                        .getFileEncoding());
            }

            log.debug("Configuration for FreemarkerResult is initiated.");
        }
    }

    /**
     * devMode값이 true일때 @{link Template}의 갱신주기를 0으로 설정하며
     * templateExceptionHandler를 {@link HTML_DEBUG_HANDLER}로 변경.
     * 
     * @param devMode
     *            개발 환경 여부
     */
    public static void setDevMode(boolean devMode) {
        if (devMode) {
            Configuration configuration = getConfiguration();
            configuration.setTemplateUpdateDelay(0);
            configuration
                    .setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);
        }
    }

    /**
     * Sets the freemarker output encoding.
     * 
     * @param outputEncoding
     *            the freemarker ouput_encoding
     */
    public static void setOutputEncoding(String outputEncoding) {
        Configuration configuration = getConfiguration();
        configuration.setOutputEncoding(outputEncoding);
    }

    /**
     * Sets the freemarker url escaping charset.
     * 
     * @param urlEscapingCharset
     *            the freemarker url_escaping_charset
     */
    public static void setURLEscapingCharset(String urlEscapingCharset) {
        Configuration configuration = getConfiguration();
        configuration.setURLEscapingCharset(urlEscapingCharset);
    }

    /**
     * Sets the template update delay.
     * 
     * @param delay
     *            the new template update delay
     */
    public static void setTemplateUpdateDelay(int delay) {
        getConfiguration().setTemplateUpdateDelay(delay);
    }

    /**
     * Sets the freemarker logger.
     * 
     * @param loggerName
     *            the new freemarker logger
     * @throws ClassNotFoundException
     *             the class not found exception
     */
    public static void setFreemarkerLogger(String loggerName)
            throws ClassNotFoundException {
        if (loggerName.equalsIgnoreCase(Constants.FREEMARKER_LOGGER_JAVA)) {
            freemarker.log.Logger
                    .selectLoggerLibrary(freemarker.log.Logger.LIBRARY_JAVA);
        } else if (loggerName
                .equalsIgnoreCase(Constants.FREEMARKER_LOGGER_LOG4J)) {
            freemarker.log.Logger
                    .selectLoggerLibrary(freemarker.log.Logger.LIBRARY_LOG4J);
        } else if (loggerName
                .equalsIgnoreCase(Constants.FREEMARKER_LOGGER_NONE)) {
            freemarker.log.Logger
                    .selectLoggerLibrary(freemarker.log.Logger.LIBRARY_NONE);
        } else if (loggerName
                .equalsIgnoreCase(Constants.FREEMARKER_LOGGER_AVALON)) {
            freemarker.log.Logger
                    .selectLoggerLibrary(freemarker.log.Logger.LIBRARY_AVALON);
        } else {
            freemarker.log.Logger
                    .selectLoggerLibrary(freemarker.log.Logger.LIBRARY_AUTO);
        }
    }

    /**
     * Sets the template exception handler.
     * 
     * @param handlerClass
     *            the new template exception handler
     * @throws ClassNotFoundException
     *             the class not found exception
     * @throws IllegalAccessException
     *             the illegal access exception
     * @throws InstantiationException
     *             the instantiation exception
     */
    public static void setTemplateExceptionHandler(String handlerClass)
            throws ClassNotFoundException, IllegalAccessException,
            InstantiationException {
        TemplateExceptionHandler exceptionHandler = (TemplateExceptionHandler) Class
                .forName(handlerClass).newInstance();
        getConfiguration().setTemplateExceptionHandler(exceptionHandler);
    }

    /**
     * {@link Configuration} 반환.
     * 
     * @return Configuration
     */
    public static Configuration getConfiguration() {
        return ConfigurationHolder.configuration;
    }

    /**
     * {@link Configuration} 초기화
     * 
     * @throws FreemarkerInitializeException
     *             the freemarker initialize exception
     */
    static void resetConfiguration() throws FreemarkerInitializeException {
        ConfigurationHolder.init();
    }

    /**
     * Sets the template loader.
     * 
     * @param templateLoader
     *            the new template loader
     * @throws FreemarkerInitializeException
     *             the freemarker initialize exception
     */
    public static void setTemplateLoader(TemplateLoader templateLoader)
            throws FreemarkerInitializeException {
        try {
            Configuration configuration = getConfiguration();
            configuration
                    .setTemplateLoader(templateLoader == null ? getDefaultTemplateLoader()
                            : templateLoader);
        } catch (Exception e) {
            throw new FreemarkerInitializeException(e);
        }
    }

    /**
     * Gets the default template loader.
     * 
     * @return the default template loader
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    private static TemplateLoader getDefaultTemplateLoader() throws IOException {
        String templateDirPath = ServletContextHolder.get().getRealPath(
                UsfConfigurationHolder.getUsfConfiguration().getTemplates());
        File templateDir = new File(templateDirPath);

        if (!templateDir.exists() || templateDir.isFile()) {
            throw new IOException("invalid template directory : "
                    + templateDirPath);
        }

        /**
         * 기본 template(.ftl) 생성을 위한 WebappTemplateLoader와 XML포맷의
         * ResourceFile(.res)을 template으로 생성하기 위한 ResourceTemplateLoader,
         * META-INF/template에 포함되어 배포되는 .ftl을 로드하기 위한 ClassTemplateLoader 를
         * 사용한다.
         */
        WebappTemplateLoader servletContextTemplateLoader = new WebappTemplateLoader(
                ServletContextHolder.get());
        ClassTemplateLoader classTemplateLoader = new ClassTemplateLoader(
                ClassTemplateLoader.class, "/META-INF/template");
        PatternResourceTemplateLoader patternResourceTemplateLoader = new PatternResourceTemplateLoader();
        TemplateLoader[] loaders = new TemplateLoader[] {
                servletContextTemplateLoader,
                new ResourceTemplateLoader(ServletContextHolder.get()),
                classTemplateLoader, patternResourceTemplateLoader };
        return new MultiTemplateLoader(loaders);
    }

}
